﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace elsimulatorcsharp
{
    class Building
    {
        private Floor[] floors; //Holds a counter of the people on the floor
        private Elevator[] elevators;

        private short highestFloor;
        private short lowestFloor;

        private byte peopleProb;

        private DateTime timer = new DateTime();


        Random rand = new Random();

        public Building(Elevator[] elevatorArr, Floor[] floorArr, byte pplProb)
        {
            this.highestFloor = (short)(floorArr.Length - 1);
            this.lowestFloor = 0;
            this.elevators = elevatorArr;
            this.floors = floorArr;
            this.peopleProb = pplProb;
        }


        public void Run() {
        if (DateTime.Now.Millisecond-timer.Millisecond > 100){
            if (rand.Next(100) > (100-peopleProb)){
                this.CreateRandomPerson();
                timer = new DateTime();
            }
        }
//        System.out.println("Entering cycle");
        foreach (Elevator elevator in elevators) {
//        System.out.println("Elevator " + elevator.getElevatorIdx() + " goes in direction " + elevator.getDirection() + " doorSize " + elevator.getDoor() + " state: "  + elevator.getState());
        switch (elevator.GetState()) {
            case Elevator.ElevatorState.MOVING:
                elevator.ChangePosition();
                if (elevator.GetProperPosition() -elevator.GetPosition() == 0 ) {
                    DoorOpeningCheck(elevator);
                }
                if (elevator.GetLastFloor() == elevator.GetPosition() 
                        && elevator.GetState() != Elevator.ElevatorState.DOOR_OPENING) {
                    elevator.SetState(Elevator.ElevatorState.IDLE);
                    elevator.SetDirection(Elevator.ElevatorDirection.NONE);
                }
//                System.out.println("Elevator Moving");
//                System.out.println(elevator.getPosition());
                break;

            case Elevator.ElevatorState.DOOR_OPENING:
                elevator.ChangeDoorSize((sbyte)1);
                if (elevator.GetDirection() == Elevator.ElevatorDirection.UP){
                        floors[elevator.GetPosition()].SetUpLock(true);
                    } else if (elevator.GetDirection() == Elevator.ElevatorDirection.DOWN) {
                        floors[elevator.GetPosition()].SetDownLock(true);
                    }
//                System.out.println("Door Opening");
                break;

            case Elevator.ElevatorState.DOOR_OPEN:
                this.MovePeople(elevator, floors[elevator.GetPosition()]);
//                System.out.println("Door Open");
                break;

            case Elevator.ElevatorState.DOOR_CLOSING:
                if (elevator.IsNotFull()){
                    DoorOpeningCheck(elevator);
                }
                if (elevator.GetState() == Elevator.ElevatorState.DOOR_CLOSING){
                    elevator.ChangeDoorSize((sbyte)-1);
                }
                
                if (elevator.IsDoorClosed()){
                    if (elevator.GetDirection() == Elevator.ElevatorDirection.UP){
                        floors[elevator.GetPosition()].SetUpLock(false);
                        floors[elevator.GetPosition()].SetUpButtonOn(false);
                    } else if (elevator.GetDirection() == Elevator.ElevatorDirection.DOWN) {
                        floors[elevator.GetPosition()].SetDownLock(false);
                        floors[elevator.GetPosition()].SetDownButtonOn(false);
                    } else if (elevator.GetState() == Elevator.ElevatorState.IDLE || elevator.GetDirection() == Elevator.ElevatorDirection.NONE){
                        floors[elevator.GetPosition()].SetUpLock(false);
                        floors[elevator.GetPosition()].SetDownLock(false);
                    }
                }
                break;

            case Elevator.ElevatorState.IDLE:
//                System.out.println("Elevator idle " + elevator.getDirection());
                elevator.SetDirection(Elevator.ElevatorDirection.NONE);
                if (elevator.GetPosition() < (highestFloor-lowestFloor)/2) {
                    if (floors[elevator.GetPosition()].IsUpButtonOn()) {
                        elevator.SetState(Elevator.ElevatorState.DOOR_OPENING);
                        elevator.SetDirection(Elevator.ElevatorDirection.UP);
                        return;
                    }
                    GetLockAboveElevator(floors, elevator, Elevator.ElevatorDirection.UP);
                    if (elevator.GetLastFloor() == elevator.GetPosition()) {
                        GetLockAboveElevator(floors, elevator, Elevator.ElevatorDirection.DOWN);
                    }
                    if (elevator.GetLastFloor() != elevator.GetPosition()) {
                        return;
                    }
                    if (floors[elevator.GetPosition()].IsDownButtonOn() && !floors[elevator.GetPosition()].IsDownLockOn()) {
                        elevator.SetState(Elevator.ElevatorState.DOOR_OPENING);
                        elevator.SetDirection(Elevator.ElevatorDirection.DOWN);
                        return;
                    }
                    GetLockBelowElevator(floors, elevator, Elevator.ElevatorDirection.DOWN);
                    if (elevator.GetLastFloor() == elevator.GetPosition()) {
                        GetLockBelowElevator(floors, elevator, Elevator.ElevatorDirection.UP);
                        return;
                    }

                }
                else {
                    if (floors[elevator.GetPosition()].IsDownButtonOn()) {
                        elevator.SetState(Elevator.ElevatorState.DOOR_OPENING);
                        elevator.SetDirection(Elevator.ElevatorDirection.DOWN);
                        return;
                    }
                    GetLockBelowElevator(floors, elevator, Elevator.ElevatorDirection.DOWN);
                    if (elevator.GetLastFloor() == elevator.GetPosition()) {
                        GetLockBelowElevator(floors, elevator, Elevator.ElevatorDirection.UP);
                    }
                    if (elevator.GetLastFloor() != elevator.GetPosition()) {
                        return;
                    }
                    if (floors[elevator.GetPosition()].IsUpButtonOn() && !floors[elevator.GetPosition()].IsUpLockOn()) {
                        elevator.SetDirection(Elevator.ElevatorDirection.UP);
                        elevator.SetState(Elevator.ElevatorState.DOOR_OPENING);
                        return;
                    }
                    GetLockAboveElevator(floors, elevator, Elevator.ElevatorDirection.UP);
                    if (elevator.GetPosition() == elevator.GetLastFloor()) {
                        GetLockAboveElevator(floors, elevator, Elevator.ElevatorDirection.DOWN);
                        return;
                    }
                }
                break;
            }
        }

        foreach (Floor floor in floors){
            List<Person> people = floor.GetPeople();
            for (int i = 0; i < people.Count; i++)
            {
                if (people.ElementAt(i).IsFinished())
                {
                    if (people.ElementAt(i).TimeSinceFinish() > 5000)
                    {
                        people.RemoveAt(i);
                    }
                }
            }
        }
    }



        protected void CreateRandomPerson()
        {
            if (rand.Next(10) >= 8)
            {
                short position = (short)(Math.Abs(rand.Next() % (this.highestFloor)) + this.lowestFloor);
                short floorDst;
                do
                {
                    floorDst = (short)(Math.Abs(rand.Next() % (this.highestFloor)) + this.lowestFloor);
                } while (floorDst == position);

                floors[position].AddPerson(new Person(floorDst));
            }
        }

        private void DoorOpeningCheck(Elevator e){
        if (!e.IsNotFull()) {
            foreach (Person person in e.GetPeople()){
                if (person.GetFloorDest() == e.GetPosition()){
                    e.SetState(Elevator.ElevatorState.DOOR_OPENING);
                    return;
                }
            }
        } else if (e.GetDirection() == Elevator.ElevatorDirection.UP && floors[e.GetPosition()].IsUpButtonOn() && !floors[e.GetPosition()].IsUpLockOn()){
            floors[e.GetPosition()].SetUpLock(true);
            //e.setLastFloor(e.getPosition());
            e.SetState(Elevator.ElevatorState.DOOR_OPENING);
        } else if (e.GetDirection() == Elevator.ElevatorDirection.DOWN && floors[e.GetPosition()].IsDownButtonOn() && !floors[e.GetPosition()].IsDownLockOn()){
            floors[e.GetPosition()].SetDownLock(true);
            //e.setLastFloor(e.getPosition());
            e.SetState(Elevator.ElevatorState.DOOR_OPENING);
        }
        foreach (Person person in floors[e.GetPosition()].GetPeople()){
            if (person.GetFloorDest() > e.GetPosition()){
                floors[e.GetPosition()].SetUpButtonOn(true);
                if (e.GetDirection() == Elevator.ElevatorDirection.UP){
                    e.SetState(Elevator.ElevatorState.DOOR_OPENING);
                }
            } else if (person.GetFloorDest() < e.GetPosition()){
                floors[e.GetPosition()].SetDownButtonOn(true);
                if (e.GetDirection() == Elevator.ElevatorDirection.DOWN){
                    e.SetState(Elevator.ElevatorState.DOOR_OPENING);
                }
            }
        }
    }

        private void MovePeople(Elevator elevator, Floor floor)
        {

            for (int i = 0; i < elevator.GetPeople().Count; i++)
            {
                if (elevator.GetPeople().ElementAt(i).GetFloorDest() == elevator.GetPosition())
                {
                    Person tempPerson = elevator.GetPeople().ElementAt(i);
                    elevator.RemovePerson(tempPerson);
                    floors[elevator.GetPosition()].AddPerson(tempPerson);
                    tempPerson.SetFinished();
                }
            }

            List<Person> people = floors[elevator.GetPosition()].GetPeople();

            if (elevator.GetDirection() == Elevator.ElevatorDirection.UP)
            {
                for (int i = 0; i < people.Count; i++)
                {
                    if (elevator.IsNotFull())
                    {
                        if (people.ElementAt(i).GetFloorDest() > elevator.GetPosition())
                        {
                            elevator.AddPerson(people.ElementAt(i));
                            floors[elevator.GetPosition()].RemovePerson(people.ElementAt(i));

                        }
                    }
                }
            }
            else if (elevator.GetDirection() == Elevator.ElevatorDirection.DOWN)
            {
                for (int i = 0; i < people.Count; i++)
                {
                    if (elevator.IsNotFull())
                    {
                        if (people.ElementAt(i).GetFloorDest() < elevator.GetPosition())
                        {
                            elevator.AddPerson(people.ElementAt(i));
                            floors[elevator.GetPosition()].RemovePerson(people.ElementAt(i));
                        }
                    }
                }
            }

            elevator.SetState(Elevator.ElevatorState.DOOR_CLOSING);

        }

        private static Person GetPersonFromFloor(Floor floor, byte direction) {
        List<Person> peopleWaiting = floor.GetPeople();
        foreach (Person person in peopleWaiting) {
            if (personWantsToGoSameWayAsElevator(person, floor.GetIndex(), direction)) {
                return person;
            }
        }
        return null;
    }

        private static bool personWantsToGoSameWayAsElevator(Person person, short floorNumber, byte direction)
        {
            if (person.GetFloorDest() - floorNumber >= direction)
            {
                return true;
            }
            else if (person.GetFloorDest() - floorNumber <= direction)
            {
                return true;
            }
            else return false;
        }

        /* private static void addPersonToRightQueue(Floor floor, int iD, Person personToQueue) {
             floor.getPersonsList().ElementAt(iD).Add(personToQueue);
         }
     */
        private static bool elevatorInUpperHalf(byte highestFloor, byte lowestFloor, Elevator elevator)
        {
            if ((byte)elevator.GetPosition() >= (byte)((highestFloor - lowestFloor) / 2))
            {
                return true;
            }
            else return false;
        }


        int GetNumberOfFloors()
        {
            return this.floors.Length;
        }

        void SetFloors(Floor[] f)
        {
            this.floors = f;
        }

        Floor[] GetFloors()
        {
            return this.floors;
        }

        void SetElevators(Elevator[] e)
        {
            this.elevators = e;
        }

        public Elevator[] GetElevators()
        {
            return this.elevators;
        }

        public short GetLowestFloor()
        {
            return lowestFloor;
        }

        public short GetHighestFloor()
        {
            return highestFloor;
        }

        public Floor GetFloor(int floorNumber)
        {
            return floors[floorNumber];
        }

        /*  void setLastFloorToGoTo(int iD, short lastFloorToGoTo) {
              lastFloorToGoToElevators[iD] = lastFloorToGoTo;
          }*/

        /*   void save() throws Exception {
               Class.forName("org.sqlite.JDBC");
               Connection conn = DriverManager.getConnection("jdbc:sqlite:saved1.db");
        
               Statement peopleDB = conn.createStatement();
               peopleDB.executeUpdate("drop table if exists People;");
               peopleDB.executeUpdate("create table People (Name, CurrentFloor, FutureFloor, Elevator, InElevator, CalledElevator, Timer);");
               PreparedStatement prepPeople = conn.prepareStatement("insert into People values (?, ?, ?, ?, ?, ?, ?)");

               for(Person person : people) {
                   prepPeople.setString(1, person.getName());
                   prepPeople.setInt(2, person.getCurrentFloor());
                   prepPeople.setInt(3, person.getFutureFloor());
                   prepPeople.setInt(4, person.getElevatorIndex());
                   prepPeople.setbool(5, person.inElevator());
                   prepPeople.setbool(6, person.hasCalledElevator());
                   prepPeople.setLong(7, person.getTimer());
                   prepPeople.addBatch();
               }

               conn.setAutoCommit(false);
               prepPeople.executeBatch();
               System.out.println("test");
               prepPeople.clearBatch();
               Statement elevatorDB = conn.createStatement();
               elevatorDB.executeUpdate("drop table if exists Elevator;");
               elevatorDB.executeUpdate("create table Elevator(Elevator_Index, "
                       + "Floors_not_allowed, Elevator_queue, Timer, Door_Timer, "
                       + "Number_of_people_inside, Capacity, Minimum_floor, Maximum_floor, "
                       + "Current_floor, Moving, Going_up, Door_closed, Door_closed_attempt, "
                       + "Emergency_stop, PRIMARY KEY (field1))");
               PreparedStatement prepElevator = conn.prepareStatement("insert into Elevator (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
               foreach (Elevator elevator in elevators) {
                   prepElevator.setInt(1, elevator.getIndex());
                   String allowedFloorsElevators = "";
                   for (bool allowedFloor : elevator.getFloorList()) {
                       allowedFloorsElevators += allowedFloor + " ";
                   }
                   prepElevator.setString(2, allowedFloorsElevators);

                   String elevatorQueue = "";
                   for (Integer wantsToGoTo : elevator.getQueue()) {
                       elevatorQueue += wantsToGoTo + " ";
                   }
                   prepElevator.setString(3, elevatorQueue);

                   prepElevator.setLong(4, elevator.getTimer());
                   prepElevator.setLong(5, elevator.getDoorTimer());
                   prepElevator.setInt(6, elevator.getPeople());
                   prepElevator.setInt(7, elevator.getCapacity());
                   prepElevator.setInt(8, elevator.getMinFloor());
                   prepElevator.setInt(9, elevator.getMaxFloor());
                   prepElevator.setInt(10, elevator.getCurrentFloor());
                   prepElevator.setbool(11, elevator.isMoving());
                   prepElevator.setbool(12, elevator.isGoingUp());
                   prepElevator.setbool(13, elevator.isDoorClosed());
                   prepElevator.setbool(14, elevator.getDoorActionAttempt());
                   prepElevator.setbool(15, elevator.getEmergencyStop());
                   prepElevator.addBatch();
               }
               prepElevator.executeBatch();

               prepElevator.clearBatch();
               Statement floorDB = conn.createStatement();
               floorDB.executeUpdate("drop table if exists Floor");
               floorDB.executeUpdate("create table Floor(Floor_name, Floor_index,"
                       + "Number_of_people_on_floor)");
               PreparedStatement prepFloor = conn.prepareStatement("insert into Floor(?, ?, ?)");

               for(Floor floor : floors) {
                   prepFloor.setString(1, floor.getFloorName());
                   prepFloor.setInt(2, floor.getIndex());
                   prepFloor.setInt(3, floor.getNumberOfPeople());
                   prepFloor.addBatch();
               }

               prepFloor.executeBatch();
               prepFloor.clearBatch();
               conn.close();
           }*/

        /*   public void load() throws Exception {
               Class.forName("org.sqlite.JDBC");
               Connection conn = DriverManager.getConnection("jdbc:sqlite:saved.db");
               Statement building = conn.createStatement();
               ResultSet accessData = building.executeQuery("select * from Floor");
               floors.clear();
               Floor.resetIndex();
               while (accessData.next()) {
                   floors.Add(new Floor(accessData.getString(1)));
                   floors.ElementAt(accessData.getInt(2)).setNumberOfPeople(
                           accessData.getInt(3));
               }

               accessData = building.executeQuery("select * from Elevator");
               elevators.clear();
               Elevator.resetIndex();

               while (accessData.next()) {
                   elevators.Add(new Elevator(accessData.getInt(8),
                           accessData.getInt(9),
                           stringToboolList(accessData.getString(2))));
                   elevators.ElementAt(elevators.Count-1).setTimer(accessData.getLong(4));
                   elevators.ElementAt(elevators.Count-1).setDoorTimer(accessData.getLong(5));
                   elevators.ElementAt(elevators.Count-1).setNumberOfPeople(accessData.getInt(6));
                   elevators.ElementAt(elevators.Count-1).setCapacity(accessData.getInt(7));
                   elevators.ElementAt(elevators.Count-1).setCurrentFloor(accessData.getInt(10));
                   elevators.ElementAt(elevators.Count-1).setMoving(accessData.getbool(11));
                   elevators.ElementAt(elevators.Count-1).setDoorClosed(accessData.getbool(13));
                   elevators.ElementAt(elevators.Count-1).setDoorClosedAttempt(accessData.getbool(14));
                   elevators.ElementAt(elevators.Count-1).setEmergencyStop(accessData.getbool(15));
                   elevators.ElementAt(elevators.Count-1).setQueue(stringToIntegerList(accessData.getString(3)));
               }

               accessData = building.executeQuery("select * from Building");
               while (accessData.next()) {
                   this.setNumberOfPeople(accessData.getInt(1));
                   this.setUpQueue(stringToIntegerList(accessData.getString(2)));
                   this.setDownQueue(stringToIntegerList(accessData.getString(3)));
                   this.setTimer(accessData.getLong(4));
                   this.setStopSimulation(accessData.getbool(5));
               }

               accessData = building.executeQuery("select * from People");
               people.clear();

               while (accessData.next()) {
                   people.Add(new Person(this, accessData.getInt(2), accessData.getInt(3)));
                   people.ElementAt(people.Count-1).setElevator(elevators.ElementAt(accessData.getInt(4)));
                   people.ElementAt(people.Count-1).setInElevator(accessData.getbool(5));
                   people.ElementAt(people.Count-1).setHasCalledElevator(accessData.getbool(6));
               }

           }*/

        /*private List<bool> stringToboolList(String boolValues)
        {
            Scanner reader = new Scanner(boolValues);
            List<bool> boolList = null;
            String test;
            while (reader.hasNext())
            {
                test = reader.next();
                if (test.equals("true"))
                {
                    boolList.Add(true);
                }
                else
                {
                    boolList.Add(false);
                }
            }
            return boolList;
        }

        private Set<Integer> stringToIntegerList(String integerValues)
        {
            Scanner reader = new Scanner(integerValues);
            Set<Integer> integerList = null;
            while (reader.hasNext())
            {
                integerList.Add(reader.nextInt());
            }
            return integerList;
        }*/

        void CreatePerson(int i, int i0)
        {
            Person person = new Person((short)i0);
            this.AddPersonToFloor(i, person);
        }

        private void AddPersonToFloor(int i, Person person)
        {
            this.GetFloors()[i].AddPerson(person);
        }

        private void GetLockAboveElevator(Floor[] floors, Elevator elevator, Elevator.ElevatorDirection elevatorDirection)
        {
            if (elevatorDirection == Elevator.ElevatorDirection.UP)
            {
                for (int i = elevator.GetPosition() + 1; i < floors.Length; i++)
                {
                    if (floors[i].IsUpButtonOn())
                    {
                        if (!floors[i].IsUpLockOn())
                        {
                            elevator.SetDirection(Elevator.ElevatorDirection.UP);
                            elevator.SetState(Elevator.ElevatorState.MOVING);
                            elevator.SetLastFloor((short)i);
                            floors[i].SetUpLock(true);
                            return;
                        }
                        else
                        {
                            //                        System.out.println("dd");
                            return;
                        }
                    }
                }
            }
            else if (elevatorDirection == Elevator.ElevatorDirection.DOWN)
            {
                for (int i = floors.Length - 1; i > elevator.GetPosition(); i--)
                {
                    if (floors[i].IsDownButtonOn())
                    {
                        if (!floors[i].IsDownLockOn())
                        {
                            elevator.SetDirection(Elevator.ElevatorDirection.DOWN);
                            elevator.SetState(Elevator.ElevatorState.MOVING);
                            //elevator.setDirectionAfterLock(ElevatorDirection.DOWN);
                            //elevator.setLock();
                            elevator.SetLastFloor((short)i);
                            floors[i].SetDownLock(true);
                            return;
                        }
                        else
                        {
                            //                        System.out.println("cc");
                            return;
                        }
                    }
                }
            }
        }

        private void GetLockBelowElevator(Floor[] floors, Elevator elevator, Elevator.ElevatorDirection elevatorDirection)
        {
            if (elevatorDirection == Elevator.ElevatorDirection.DOWN)
            {
                for (int i = elevator.GetPosition() - 1; i > lowestFloor; i--)
                {
                    if (floors[i].IsDownButtonOn())
                    {
                        if (!floors[i].IsDownLockOn())
                        {
                            elevator.SetDirection(Elevator.ElevatorDirection.DOWN);
                            elevator.SetState(Elevator.ElevatorState.MOVING);
                            elevator.SetLastFloor((short)i);
                            floors[i].SetDownLock(true);
                            return;
                        }
                        else
                        {
                            //                        System.out.println("bb");
                            return;
                        }
                    }
                }
            }
            else if (elevatorDirection == Elevator.ElevatorDirection.UP)
            {
                for (int i = lowestFloor; i < elevator.GetPosition(); i++)
                {
                    if (floors[i].IsUpButtonOn())
                    {
                        if (!floors[i].IsUpLockOn())
                        {
                            elevator.SetDirection(Elevator.ElevatorDirection.UP);
                            elevator.SetState(Elevator.ElevatorState.MOVING);
                            //elevator.setLock();
                            //elevator.setDirectionAfterLock(ElevatorDirection.UP);
                            elevator.SetLastFloor((short)i);
                            floors[i].SetUpLock(true);
                            return;
                        }
                        else
                        {
                            //                        System.out.println("aa");
                            return;
                        }
                    }
                }
            }
        }

        protected short GetElevatorCount()
        {
            return (short)elevators.Length;
        }

        public double[] GetElevatorPositions() {
        double[] elevatorPositions = new double[elevators.Length];
        int i = 0;
        foreach (Elevator elevator in elevators) {
            elevatorPositions[i] = elevator.GetProperPosition();
            i++;
        }
        return elevatorPositions;
    }

        public double[] GetDoorOpenings() {
        double[] doorOpenings = new double[elevators.Length];
        int i = 0;
        foreach (Elevator elevator in elevators) {
            doorOpenings[i]=elevator.GetDoor();
            i++;
        }
        return doorOpenings;
        
    }

        void SetPeopleProbability(byte p)
        {
            this.peopleProb = p;
        }
    }
}
